// CanvasWnd.cpp: implementation of the CCanvasWnd class.
//
//////////////////////////////////////////////////////////////////////

#include "CanvasWnd.h"
#include "CanvasContainer.h"
#include <math.h>
#include <assert.h>


#define DEFAULT_FONT "Courier New"

static HFONT PrepareFont(const Text& txt);
#define TURTLE_HIEGHT 16
#define TURTLE_WIDTH  12
#define MAX(a,b) (a>b? a: b)

#define DELAY_TIMER  (10)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define TURTLE_COLOR    RGB(56, 127, 56)

CCanvasWnd::CCanvasWnd()
{
    m_doc.SetCanvas(this);
    m_container = NULL;
    m_commander = NULL;
    m_delay = 0;
    m_uDelayTimer = 0;
}

CCanvasWnd::~CCanvasWnd()
{
}

void CCanvasWnd::SetContainer(ICanvasContainer* ctn)
{
    assert (ctn);
    m_container = ctn;
}

void CCanvasWnd::SetExecCommander(IExecCommander* commander)
{
    assert (commander);
    m_commander = commander;
}

int CCanvasWnd::GetTextRect(const Text& txt, RECT& rc)
{
    SIZE sz;    
    HDC hdc = GetDC();
    HFONT hFont = PrepareFont(txt);
    HFONT hOldont = (HFONT)SelectObject(hdc, hFont);
    SetTextAlign(hdc, TA_TOP | TA_LEFT);

    GetTextExtentPoint32 (hdc, txt.ptext, strlen(txt.ptext), &sz);
    rc.left = txt.position.x;
    rc.top = txt.position.y;
    rc.right = rc.left + sz.cx;
    rc.bottom = rc.top + sz.cy;

    ReleaseDC(hdc);
    DeleteObject (hFont);
    return 0;
}

void CCanvasWnd::DrawStroke(CStroke* stk, HDC hdc/*=NULL*/)
{
    bool bRelease = false;
    if (!hdc) {
        hdc = GetDC();
        bRelease = true;
    }

    if (stk->m_bText) {
        DrawStkText (hdc, stk);
    } else {
        DrawStkLine (hdc, stk);
    }

    if (bRelease) {
        ReleaseDC(hdc);
    }
}

void CCanvasWnd::DrawTurtle ()
{
	m_wndTurtle.DrawTurtle(&m_doc);
}

void CCanvasWnd::DrawStkLine (HDC hdc, CStroke* stk)
{
    HPEN hOldPen, hPen;
    
    hPen = CreatePen(PS_SOLID, stk->m_line.penwidth, stk->m_line.pencolor);
    hOldPen = (HPEN)SelectObject(hdc, hPen);
    MoveToEx (hdc, stk->m_line.from.x, stk->m_line.from.y, NULL);
    LineTo (hdc, stk->m_line.to.x, stk->m_line.to.y);

    if (m_doc.m_bShowTurtle) {
		DrawTurtle ();
	}

    SelectObject (hdc, hOldPen);
    DeleteObject (hPen);
}

void CCanvasWnd::DrawStkText (HDC hdc, CStroke* stk)
{
    HFONT hFont, hOldFont;

    assert (stk->m_bText);

    hFont = PrepareFont(stk->m_text);
    hOldFont = (HFONT)SelectObject (hdc, hFont);
    SetTextAlign(hdc, TA_TOP | TA_LEFT);
    SetBkMode(hdc, TRANSPARENT);
    COLORREF oldClr = SetTextColor (hdc, stk->m_text.pencolor);
    TextOut(hdc, stk->m_text.position.x, stk->m_text.position.y, stk->m_text.ptext, strlen (stk->m_text.ptext));

    if (m_doc.m_bShowTurtle) {
		DrawTurtle ();
	}

    SetTextColor (hdc, oldClr);
    SelectObject (hdc, hOldFont);
    DeleteObject (hFont);
}

void CCanvasWnd::SetErrorInfoWindow(HWND hErrWnd)
{
    m_doc.SetErrInfoWnd(hErrWnd);
}

LRESULT CCanvasWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
   m_doc.SetCanvasWnd(m_hWnd);
	RECT rc;
	GetClientRect(&rc);
	m_wndTurtle.CreateTurtleWnd(m_hWnd, rc);
	m_wndTurtle.DrawTurtle(&m_doc);
   return 0;
}

LRESULT CCanvasWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	m_wndTurtle.DestroyWindow();
    return 0;
}

LRESULT CCanvasWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	RECT rc;
	GetClientRect(&rc);
	m_wndTurtle.MoveWindow(0, 0, rc.right - rc.left, rc.bottom - rc.top);
    Invalidate(TRUE);
    m_wndTurtle.Invalidate();
    return 0;
}

LRESULT CCanvasWnd::OnMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    Invalidate(TRUE);
    m_wndTurtle.Invalidate();
    return 0;
}

LRESULT CCanvasWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	PAINTSTRUCT ps;
	BeginPaint(&ps);

	RECT rc = ps.rcPaint;
	HDC hDC = ps.hdc;
    HBRUSH hBrush;
    hBrush = CreateSolidBrush(m_doc.m_clrCanvas);
    FillRect (hDC, &rc, hBrush);
    
    m_doc.DrawAllStrokes(&rc);
    
    DeleteObject(hBrush);

    EndPaint(&ps);
	return 0;
}

LRESULT CCanvasWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if (wParam == m_uDelayTimer) {
        KillTimer(m_hWnd, m_uDelayTimer);
        m_uDelayTimer = 0;
        ::PostThreadMessage(m_uThread, MSG_EX_ACK, 0, 0);
    }
    return 0;
}

LRESULT CCanvasWnd::OnCs(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    DblPoint* pt = (DblPoint*)wParam;
    assert (pt);
    assert (m_container);

    m_doc.canvassize(pt->x, pt->y);

    RECT rc = {0, 0, 0, 0};    
    rc.right = (LONG)(pt->x);
    rc.bottom = (LONG)(pt->y);
    MoveWindow(&rc);
    m_container->CanvasSizeChanged();

    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnReset(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.reset();
    
    RECT rc = {0, 0, 0, 0};    
    rc.right = m_doc.m_uCanvasWidth;
    rc.bottom = m_doc.m_uCanvasHeight;
    MoveWindow(&rc);
    m_container->CanvasSizeChanged();

    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnClear(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.clear();

    Invalidate();
    UpdateWindow(); // enforce an immediate update.
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnHide(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.hide();
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnCc(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    DblRGB* rgb = (DblRGB*)wParam;
    m_doc.canvascolor(rgb->r, rgb->g, rgb->b);
    Invalidate();
    UpdateWindow();  // enforce an immediate update.
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnPc(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    DblRGB* rgb = (DblRGB*)wParam;
    m_doc.pencolor(rgb->r, rgb->g, rgb->b);
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnPw(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* width = (double*)wParam;
    m_doc.penwidth(*width);
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnGo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    DblPoint* pt = (DblPoint*)wParam;
    m_doc.go(pt->x, pt->y);

    DrawTurtle();

    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnDir(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* direction = (double*)wParam;
    m_doc.direction(*direction);
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnTr(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* direction = (double*)wParam;
    m_doc.turnright(*direction);
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnTl(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* direction = (double*)wParam;
    m_doc.turnleft(*direction);
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnFw(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* dst = (double*)wParam;
    m_doc.forward(*dst);
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnBw(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* dst = (double*)wParam;
    m_doc.backward(*dst);
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnFontsize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* fs = (double*)wParam;
    m_doc.fontsize(*fs);
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnPrint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    char* str = (char*)wParam;
    m_doc.print(str);
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnPu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.penup();
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnPd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.pendown();
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnCenter(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.center();
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnGox(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* x = (double*)wParam;
    m_doc.gox(*x);

    DrawTurtle();

    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnGoy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    double* y = (double*)wParam;
    m_doc.goy(*y);

    DrawTurtle();
    
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnWrapOn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.wrapon();
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnWrapOff(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.wrapoff();
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnShow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.show();
    DrawTurtle();
    SetDelayTimer(lParam);    
    return 0;
}

LRESULT CCanvasWnd::OnInput(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    char* ret = m_doc.inputwindow((char*)wParam);
    ::PostThreadMessage(lParam, MSG_EX_ACK, (WPARAM)ret, 0);
    return 0;
}

LRESULT CCanvasWnd::OnMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.message((char*)wParam);
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnDbgInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.debug_info((char*)wParam);
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

LRESULT CCanvasWnd::OnErrorInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_doc.error_info((char*)wParam);
    ::PostThreadMessage(lParam, MSG_EX_ACK, 0, 0);
    return 0;
}

void CCanvasWnd::SetDelayTimer(DWORD thread)
{
    assert (m_uDelayTimer == 0);
    m_uThread = thread;
    if (m_delay) {
        m_uDelayTimer = SetTimer(m_hWnd, DELAY_TIMER, m_delay, NULL);
        return;
    }
    ::PostThreadMessage(thread, MSG_EX_ACK, 0, 0);
}

LRESULT CCanvasWnd::OnDone(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    assert (m_commander);
    m_commander->ExecutionDone();
    return 0;
}

LRESULT CCanvasWnd::OnDebugAck(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    assert (m_commander);
    m_commander->DebugCommandFinished(wParam);
    return 0;
}
// helper
static HFONT PrepareFont (const Text& text)
{
    LOGFONT lf;
    HFONT hFont;
    lf.lfCharSet = ANSI_CHARSET;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfEscapement = 0;
    strncpy (lf.lfFaceName, DEFAULT_FONT, LF_FACESIZE);
    lf.lfHeight = text.fontsize;
    lf.lfItalic = 0;
    lf.lfOrientation = 0;
    lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
    lf.lfPitchAndFamily = DEFAULT_PITCH;
    lf.lfQuality = DEFAULT_QUALITY;
    lf.lfStrikeOut = 0;
    lf.lfUnderline = 0;
    lf.lfWeight = FW_NORMAL;
    lf.lfWidth = 0;
    hFont = CreateFontIndirect(&lf);
    return hFont;
}
